package core;

import java.util.*;

import csp.BacktrackSearch;
import data.*;
import data.Binary.BinaryType;
import data.Unary.UnaryType;

public class Program {
	private enum LoadState {
		None,
		Variables,
		Values,
		FittingLimits,
		UnaryInclusive,
		UnaryExclusive,
		BinaryEquals,
		BinaryNotEquals,
		MutualExclusive;
		
		public LoadState Increment() {
			return values()[ordinal() + 1];
		}
	}
	
	private static LoadState State;
	private static HashMap<String, Item> Variables;
	private static HashMap<String, Bag> Values;
	private static FittingLimit FittingLimit;
	private static ArrayList<Unary> UnaryConstraints;
	private static ArrayList<Binary> BinaryConstraints;
	private static ArrayList<MutualExclusive> MutualExclusiveConstraints;
	
	public static void main(String[] args) {
		State = LoadState.None;
		Variables = new HashMap<String, Item>();
		Values = new HashMap<String, Bag>();
		UnaryConstraints = new ArrayList<Unary>();
		BinaryConstraints = new ArrayList<Binary>();
		MutualExclusiveConstraints = new ArrayList<MutualExclusive>();
		try {
			Input.OpenFile(args[0]);
			LoadFile();
		} catch (Exception e) {
			Output.Write(e.toString());
			Input.Close();
			return;
		}
		BacktrackSearch bts = new BacktrackSearch(Variables, Values, FittingLimit, BacktrackSearch.HIGH);
		bts.trace=true;
		BacktrackSearch.printSolution(bts.search(), bts.consistencyChecks);
	}
	
	private static void LoadFile() throws Exception {
		String line = Input.NextLine();
		while (line != null) {
			if ((line.length()>=5)&&(line.substring(0, 5).equals("#####"))) {
				State = State.Increment();
				line=Input.NextLine();
				continue;
			}
			
			switch (State) {
			case Variables:
				Item item = CreateItem(line);
				Variables.put(item.Name, item);
				break;
			case Values:
				Bag bag = CreateBag(line);
				Values.put(bag.Name, bag);
				break;
			case FittingLimits:
				FittingLimit = CreateFittingLimit(line);
				//CreateFittingLimit(line);
				break;
			case UnaryInclusive:
				UnaryConstraints.add(CreateUnary(UnaryType.Inclusive, line));
				break;
			case UnaryExclusive:
				UnaryConstraints.add(CreateUnary(UnaryType.Exclusive, line));
				break;
			case BinaryEquals:
				BinaryConstraints.add(CreateBinary(BinaryType.Equal, line));
				break;
			case BinaryNotEquals:
				BinaryConstraints.add(CreateBinary(BinaryType.NotEqual, line));
				break;
			case MutualExclusive:
				MutualExclusiveConstraints.add(CreateMutualExclusive(line));
				break;
			}
			line = Input.NextLine();
		}
	}
	
	private static Item CreateItem(String data) {
		String[] parts = data.split(" ");
		return new Item(parts[0], Integer.parseInt(parts[1]));
	}
	
	private static Bag CreateBag(String data) {
		String[] parts = data.split(" ");
		Bag bag = new Bag(parts[0], Integer.parseInt(parts[1]));
		
		for (Item item : Variables.values()) {
			item.Domain.add(bag);
		}
		
		return bag;
	}
	
	private static FittingLimit CreateFittingLimit(String data) {
		String[] parts = data.split(" ");
		FittingLimit fittingLimit = new FittingLimit(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]));
		
		for (Item item : Variables.values()) {
			item.FittingLimit = fittingLimit;
		}
		
		return fittingLimit;
	}
	
	private static Unary CreateUnary(UnaryType type, String data) {
		ArrayList<Bag> values = new ArrayList<Bag>();
		String[] parts = data.split(" ");
		
		Item item = Variables.get(parts[0]);
		
		for (int i = 1; i < parts.length; i++) {
			Bag value = Values.get(parts[i]);
			values.add(value);
		}
		
		Unary unary = new Unary(type, item, values);
		item.UnaryConstraints.add(unary);
		
		return unary;
	}
	
	private static Binary CreateBinary(BinaryType type, String data) {
		String[] parts = data.split(" ");

		Item variableA = Variables.get(parts[0]);
		Item variableB = Variables.get(parts[1]);
		
		Binary binary = new Binary(type, variableA, variableB);
		variableA.BinaryConstraints.add(binary);
		variableB.BinaryConstraints.add(binary);
		
		return binary;
	}
	
	private static MutualExclusive CreateMutualExclusive(String data) {
		String[] parts = data.split(" ");

		Item variableA = Variables.get(parts[0]);
		Item variableB = Variables.get(parts[1]);
		Bag valueA = Values.get(parts[0]);
		Bag valueB = Values.get(parts[1]);
		
		MutualExclusive mutualExclusive = new MutualExclusive(variableA, variableB, valueA, valueB);
		variableA.MutualExclusiveConstraints.add(mutualExclusive);
		variableB.MutualExclusiveConstraints.add(mutualExclusive);
		
		return mutualExclusive;
	}
}
